from django.shortcuts import render

# Create your views here.
from rest_framework.response import Response

from carts.serializers import CartSerializer, CartSKUSerializer, CartDeleteSerializer
from goods.models import SKU

""""
购物车信息的保存
判断是登陆用户还是为登陆用户

登陆用户保存到redis中，未登陆用户保存到

保存的三个数据　商品的id(sku_id)  商品的数量(count) 是否选中(selected)

登陆用户保存在redis中的数据格式
    保存商品id和商品的数量格式　hash　
        当前用户表示：　商品的id：　商品的数量
        cart_1 : 1: 2
    保存商品的选中状态，我们只需要保存选中的商品的id，使用数据格式　set
        是否选中标识：　选中的商品的id
        selected : {1, 2, 3}

未登录的cookie的处理　需要进行加密
    {
        商品的id : {商品的数量:xxx, 商品的选中状态:xxx}
        1 : {'count':xxx, 'selected':xxx}
    }
    我们对数据进行加密，使用base64加密，base64只能对二进制进行加密
    使用pickle模块转换
        pickle.dumps() 将数据转换成二进制类型
        pickle.loads() 将二进制数据反序列化成数据

"""

"""
用户在商品详情页面，点击添加到购物车的时候，前端发送一个ajax请求，将商品的id(sku_id), 用户添加的数量(count)，　jwt(k可选)，　selected(可选，是否选中状态)

接收参数，对数据进行校验
获取sku_id,count,selected
获取用户，根据登陆信息判断登陆状态
登陆用户设置redis
    链接到redis
    保存数据到redis中
    返回响应
未登陆用户设置cookie
    先读取cookie，判断cookie中是否有购物车信息
        如果有，则读取cookie，没有则忽略
    更新购物车信息
    需要对购物车进行处理（加密）
    返回响应　设置cookie


请求方式　post
请求路径　cart/
请求参数　商品的id，　添加的数量, 选中状态，
返回响应　登陆用户返回字典数据，未登陆用户返回字典数据和cookie
"""

from rest_framework.views import APIView
from django_redis import get_redis_connection
import base64
import pickle


class CartView(APIView):
    #  如果传递的token有问题，则用户无法访问添加购物车这个视图
    #  我们规定用户在jwt的token有问题是还是让用户访问，在用户进行下单的时候必须进行登陆
    #  我们先不进行身份认证
    def perform_authentication(self, request):
        """
        重写　perform_authentication方法，取消对用户的认证
        """
        pass

    def post(self, request):
        # 接收参数，对数据进行校验
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 获取sku_id,count,selected
        #  serializer.data(序列化器序列化完成返回的数据) = serializer.validated_data（校验完成后的数据） 是等同的　
        sku_id = serializer.data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.data.get('selected')
        # 获取用户，根据登陆信息判断登陆状态
        try:
            user = request.user
        except Exception:
            user = None
        # 判断是否是登陆用户　is_authenticated 表示用户是否检验通过，校验通过返回True
        if user is not None and user.is_authenticated:
            # 登陆用户设置redis
            #     链接到redis
            redis_conn = get_redis_connection('carts')
            #  pipeline优化redis储存，缓存，一次性提交
            p = redis_conn.pipeline()
            #     保存数据到redis中
            # 记录购物车商品id和商品数量
            p.hincrby('cart_%s' % user.id, sku_id, count)
            if selected:
                p.sadd('cart_selected_%s' % user.id, sku_id)
            #  执行缓存，提交
            p.execute()
            # 返回响应
            return Response(serializer.data)
        else:
            # 未登陆用户设置cookie
            cart_str = request.COOKIES.get('cart')
            #     先读取cookie，判断cookie中是否有购物车信息
            #         如果有，则读取cookie，没有则忽略
            if cart_str is not None:
                #  进行解码获取cookie信息
                # {16: {'count': 1, 'selected': True}}
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}
            # 更新购物车信息
            #    如果有相同的商品，求和
            if sku_id in cart_dict:
                #  {16: {'count': 1, 'selected': True}}
                origin_count = cart_dict[sku_id]['count']
                count += origin_count
            # 需要对购物车进行处理（加密）
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }
            #  设置返回的cookie数据
            response = Response(serializer.data)
            cookie_cart = base64.b64encode(pickle.dumps(cart_dict)).decode()
            response.set_cookie('cart', value=cookie_cart)
            #     返回响应　设置cookie
            return response

    """
    用户点击我的购物车的时候，前端发送一个get请求，获取购物车的所有数据

    获取用户信息
    判断登陆用户还是非登陆用户
    登陆用户从redis中获取购物车信息
        链接redis
        获取当前用户的购物车信息
        获取所有的商品信息
        对数据进行序列化
        返回响应
    非登陆用户从cookie获取信息
        从cookie中获取信息
        判断cookie是否为空
        从cookie中获取商品的id
        获取所有商品的信息
        对数据进行序列化

    请求方式　　GET
    请求路径　　cart/
    请求参数　　无
    返回响应　　商品的数据和数量和选中状态
    """

    def get(self, request):
        # 获取用户信息
        try:
            user = request.user
        except Exception:
            user = None
        # 判断登陆用户还是非登陆用户
        if user is not None and user.is_authenticated:
            # 登陆用户从redis中获取购物车信息
            #     链接redis
            redis_conn = get_redis_connection('carts')
            #     获取当前用户的购物车信息
            #  获取当前用户的商品和数量   {sku_id:{1:2}, sku_id:{2:5}}
            cart = redis_conn.hgetall('cart_%s' % user.id)
            #  获取当前用户的选中列表
            cart_selected = redis_conn.smembers('cart_selected_%s' % user.id)
            #  与下面方法一样，实现代码抽取，转换成下面一样的格式{16:{count:20,selected:True}, 12:{count:10,selected:False}}
            cart_dict = {}
            for sku_id, count in cart.items():
                #  从redis中获取的是二进制类型，需要转换成int类型
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    #  商品id在色号选中列表中
                    'selected': sku_id in cart_selected
                }
        else:
            # 非登陆用户从cookie获取信息
            #     从cookie中获取信息
            cart_str = request.COOKIES.get('cart')
            #     判断cookie是否为空
            if cart_str is not None:
                #      对cookie进行解密
                #     cookie的数据格式{16:{count:20,selected:True}, 12:{count:10,selected:False}}
                decode = base64.b64decode(cart_str)
                cart_dict = pickle.loads(decode)
            else:
                cart_dict = {}
        # 获取所有商品的信息
        skus = SKU.objects.filter(id__in=cart_dict.keys())

        #  数据库中报告没有count和selected属性，所有在响应的的时候会报错
        #  所有我们需要设置count和selected的属性
        for sku in skus:
            sku.count = cart_dict[sku.id]['count']
            sku.selected = cart_dict[sku.id]['selected']
        # 对数据进行序列化
        serializer = CartSKUSerializer(skus, many=True)
        #     返回响应
        return Response(serializer.data)

    """
    修改购物车数据，当用户修改购物车的数量和选中状态的的时候，前端发送一个ajax请求，携带参数sku_id, count,selected
    我们修改购物的的数量是用户修改完毕后的数量

    接收并校验参数
    获取参数
    获取当前用户
    判断用户的登陆状态
    登陆状态修改redis数据
        链接redis
        更新数据，hash数据和ste数据
        返回响应
    非登陆状态修改cookie数据
        获取cookie，
        解密cookie,得到cookie的字典数据
        判断当前商品是否在cookie在
            在cookie中修改cookie数据
        加密cookie
        返回响应

    请求方式　　put
    请求路径　　cart/
    请求参数　　商品id(sku_id)  商品数量(count)  选中状态(selected)
    返回响应　　商品数据，数量，选中状态


    """

    def put(self, requset):
        # 接收并校验参数
        serializer = CartSerializer(data=requset.data)
        serializer.is_valid(raise_exception=True)
        # 获取参数
        sku_id = serializer.data.get('sku_id')
        count = serializer.data.get('count')
        selected = serializer.data.get('selected')
        # 获取当前用户
        try:
            user = requset.user
        except Exception:
            user = None
        # 判断用户的登陆状态
        if user is not None and user.is_authenticated:
            # 登陆状态修改redis数据
            #     链接redis
            redis_conn = get_redis_connection('carts')
            #  pipeline优化redis储存，缓存，一次性提交
            p = redis_conn.pipeline()
            #     更新数据，hash数据和ste数据
            #   更新hash数据，set直接覆盖之前的数据
            p.hset('cart_%s' % user.id, sku_id, count)
            #   更新set数据
            if selected:
                #  如果selected为True，选中状态，则想选中列表中添加商品id
                p.sadd('cart_selected_%s' % user.id, sku_id)
            else:
                #  如果selected为False,未选中状态，则删除该列表的商品id
                p.srem('cart_selected_%s' % user.id, sku_id)
            #  执行缓存，提交
            p.execute()
            # 返回响应
            return Response(serializer.data)
        else:
            # 非登陆状态修改cookie数据
            #     获取cookie
            cart_str = requset.COOKIES.get('cart')
            #     判断cookie是否为空
            if cart_str is not None:
                #     不为空解密cookie,得到cookie的字典数据
                cart_dict = pickle.loads(base64.b64decode(cart_str))
            else:
                cart_dict = {}
            # 判断当前商品是否在cookie在
            if sku_id in cart_dict:
                #      在cookie中修改cookie数据
                cart_dict[sku_id] = {
                    'count': count,
                    'selected': selected
                }
            # 加密cookie
            new_cookie = base64.b64encode(pickle.dumps(cart_dict)).decode()
            #     返回响应
            response = Response(serializer.data)
            response.set_cookie('cart', new_cookie)
            return response

    """
    用户点击删除的时候，前端发送一个ajax请求，携带参数sku_id
    获取参数并进行校验
    获取参数信息
    获取用户信息
    登陆用户删除redis数据
        链接redis
        删除对应的数据
        返回响应
    非登陆用户删除cookie数据
        获取cookie
        判断是否为空，不为空进行解码
        判断商品id是否在cookie里面
            在cookie里面删除改信息
            组织cookie数据
        返回响应

    请求方式　　delete
    请求路径　　cart/
    请求参数　　商品id(sku_id)
    返回响应　　商品id
    """

    def delete(self, request):
        # 获取参数并进行校验
        serializer = CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 获取参数信息
        sku_id = serializer.data.get('sku_id')
        # 获取用户信息
        try:
            user = request.user
        except Exception:
            user = None
        if user is not None and user.is_authenticated:
            # 登陆用户删除redis数据
            #     链接redis
            redis_conn = get_redis_connection('carts')
            #  pipeline优化redis储存，缓存，一次性提交
            p = redis_conn.pipeline()
            #     删除对应的数据
            p.hdel('cart_%s' % user.id, sku_id)
            p.srem('cart_selected_%s' % user.id, sku_id)
            #  执行缓存，提交
            p.execute()
            #     返回响应
            return Response(serializer.data)
        else:
            # 非登陆用户删除cookie数据
            #     获取cookie
            cart_str = request.COOKIES.get('cart')
            #     判断是否为空，不为空进行解码
            if cart_str is not None:
                cart_dict = pickle.loads(base64.b64decode(cart_str))
            else:
                cart_dict = {}
            #     判断商品id是否在cookie里面
            if sku_id in cart_dict:
                #    在cookie里面删除改信息
                del cart_dict[sku_id]
            #    组织cookie数据
            new_cookie = base64.b64encode(pickle.dumps(cart_dict)).decode()
            #     返回响应
            response = Response(serializer.data)
            response.set_cookie('cart', new_cookie)
            return response
